package edu.hawaii.duedates;

import java.util.Calendar;
import java.util.List;
import java.util.ArrayList;
import edu.hawaii.duedates.lender.BorrowedItem;
import edu.hawaii.duedates.lender.Lender;
import edu.hawaii.duedates.lender.LogInException;
import edu.hawaii.duedates.lender.ParseItemException;

/**
 * Console version of DueDates project.
 * 
 * @author Daniel Tian
 *
 */
public class Console {
  private String[] arguments;
  private DueDates dueDates;
  private List<String> fieldList;
  private List<String> sortList;
  private Calendar withinDate;
  private int numOfDays;
  private boolean errorFlag;
  private String emailAddress;
  private String smtpServer;
  private boolean consoleOutput = false;
  private boolean emailOutput;
  
  /**
   * Constructor.
   */
  public Console() {
    this.setArguments(new String[]{});
  }
  
  /**
   * Constructor. Sets the command-line arguments.
   * 
   * @param callingDueDatesObject The due dates object that called this console. Awesome.
   * @param arguments The command-line arguments.
   */
  public Console(DueDates callingDueDatesObject, String[] arguments) {
    this.dueDates = callingDueDatesObject;
    this.setArguments(arguments);
  }
  
  /**
   * Resets the console and creates a local reference to the command-line arguments.
   * 
   * @param arguments The command-line arguments.
   */
  public final void setArguments(String[] arguments) {
    this.fieldList = new ArrayList<String>();
    this.sortList = new ArrayList<String>();
    this.arguments = arguments.clone();
    this.errorFlag = false;
  }
  
  /**
   * Parses all the command-line arguments and performs all the necessary commands.
   */
  public void parseArguments() {
    if (arguments.length < 0) {
      System.out.println("No arguments provided.");
      this.printUsageInfo();
      this.errorFlag = true;
    }
    else {
      System.out.println("Please wait...");
      
      for (int i = 0; i < arguments.length; i++) {
        // -hsl flag
        if ("-hsl".equals(arguments[i])) {
          i = this.parseFields(++i);
          
          if (this.fieldList.size() == 2) {
            this.dueDates.createHawaiiStatePublicLibrary(fieldList.get(0), fieldList.get(1));
          }
          else {
            System.out.println("ERROR: -hsl flag takes 2 arguments: ID number, and last 4 digits " +
            "of phone number");
          this.errorFlag = true;
          }
        }
        // -uhm flag
        else if ("-uhm".equals(arguments[i])) {
          i = this.parseFields(++i);
          
          if (this.fieldList.size() > 2) {
            System.out.println("ERROR: -uhm flag takes 2 arguments: ID number, and last name");
            this.errorFlag = true;
          }
          else {
            this.dueDates.createUhManoaLibrary(fieldList.get(0), fieldList.get(1));
          }
        }
        // -sort flag
        else if ("-sort".equals(arguments[i])) {
          i = this.parseFields(++i);
          
          if (this.fieldList.isEmpty()) {
            System.out.println("ERROR: -sort type not provided. Available choices are 'library' " +
              "and 'duedates'");
            this.errorFlag = true;
          }
          if (this.fieldList.contains("duedates")) {
            this.sortList.add("duedates");
          }
          
          this.sortList.add("library"); // always sort by library
        }
        // -within flag
        else if ("-within".equals(arguments[i])) {
          i = this.parseFields(++i);
          
          if (this.fieldList.isEmpty() || this.fieldList.size() > 1) {
            System.out.println("ERROR: -within flag takes one integer value");
            this.errorFlag = true;
          }
          else {
            try {
              int days = Integer.parseInt(fieldList.get(0));
            
              // if days is greater than zero, set it up, otherwise if it's 0, don't do anything
              if (days > 0) {
                this.withinDate = Calendar.getInstance();
                this.withinDate.add(Calendar.DATE, days);
                this.numOfDays = days;
              }
            }
            catch (NumberFormatException e) {
              System.out.println("ERROR: -within flag number must be an integer");
            }
          }
        }
        else if ("-version".equals(arguments[i]) || "-v".equals(arguments[i])) {
          System.out.println("DueDates version 1.2");
        }
        else if ("-email".equals(arguments[i])) {
          i = this.parseFields(++i);
          if (this.fieldList.isEmpty() || this.fieldList.size() > 2) {
            System.out.println("-email flag takes 2 String values.");
            this.errorFlag = true;
          }
          else {
            this.emailOutput = true;
            this.emailAddress = fieldList.get(0);
            this.smtpServer = fieldList.get(1);
          }            
        }
        else if ("-console".equals(arguments[i])) {
          this.consoleOutput = true;
        }
        else if ("-wakeup".equals(arguments[i])) {
          i = this.parseFields(++i);
          if (this.fieldList.isEmpty() || (this.fieldList.size() > 1)
              || (Double.valueOf(this.fieldList.get(0)) < 0)) {
            System.out.println("-wakeup flag takes one non-negative numberic value.");
            this.errorFlag = true;
          }
          else {
            this.dueDates.setWakeupValue(Double.valueOf(this.fieldList.get(0)));
          }
        }
        // If none of the flags were matched
        else {
          System.out.println("Invalid option: " + arguments[i]);
          this.errorFlag = true;
        }
      }
      if (!this.consoleOutput && ((this.emailAddress == null) || (this.smtpServer == null))) {
        this.errorFlag = true;
      }
      
      // sort by the type specified by the user
      if (this.sortList.contains("library")) {
        this.dueDates.sortByLenderName();
      }
      if (this.sortList.contains("duedates")) {
        this.dueDates.sortByDueDate();
      }
      
      try {
        this.dueDates.logInAllLenders();
        this.dueDates.queryAllLenders();
      }
      catch (LogInException e) {
        System.out.println("ERROR: could not log into " + e.getMessage());
        this.errorFlag = true;
      }
      catch (ParseItemException e) {
        System.out.println("ERROR could not parse " + e.getMessage());
        this.errorFlag = true;
      }
    }
  }
  
  /**
   * Gets all the arguments in between two commands, denoted by a dash, e.g. -uhm, -sort.
   * 
   * @param i The array position right after the current command.
   * @return The array position right before the next command, we return the position right before
   * because the loop will increment it.
   */
  private int parseFields(int i) {
    int arrayPosition = i;
    fieldList.clear();
    
    while (arrayPosition < arguments.length && !arguments[arrayPosition].startsWith("-")) {
      this.fieldList.add(arguments[arrayPosition++]);
    }
    
    return --arrayPosition;
  }
  
  /**
   * Returns error flag value.
   * 
   * @return errorFlag Error flag value.
   */
  public boolean isErrorOccurred() {
    return this.errorFlag;
  }
  
  /**
   * Prints all the borrowed items to the console.
   * 
   * @return A string containing the printout of the due dates object.
   */
  public String printBorrowedItems() {
    List<Lender> lenderList = this.dueDates.getLenderList();
    List<BorrowedItem> itemList;
    StringBuffer result = new StringBuffer(300);
    //System.out.println();
    
    // if there is a within date, print out the number of days the books are due within
    if (this.withinDate != null) {
      if (this.numOfDays == 1) {
        result.append("Items due within ").append(this.numOfDays).append(" day:\n");
        //System.out.println("Items due within " + this.numOfDays + " day:");
      }
      else {
        result.append("Items due within ").append(this.numOfDays).append(" days:\n");
        //System.out.println("Items due within " + this.numOfDays + " days:");
      }
    }
    
    for (Lender lender : lenderList) {
      result.append(lender.getLenderName()).append(": \n");
      //System.out.println("\n" + lender.getLenderName() + ":");
      
      if (this.withinDate == null) {
        itemList = lender.getItemList();
      }
      else {
        itemList = lender.getItemListWithinDate(withinDate);
      }
      
      if (itemList.isEmpty()) {
        result.append("There are no items due.\n");
        //System.out.println("There are no items due.");
      }
      else {
        result.append("Due Date   | Title\n");
        //System.out.println("Due Date   | Title");
        
        for (BorrowedItem item : itemList) {
          String formattedName = item.getItemName();
          formattedName = formattedName.replaceAll("/.*", "");
          formattedName = formattedName.replaceAll("[;|]", "");
          formattedName = formattedName.trim(); // required to trim off trailing spaces
          
          if (formattedName.length() < 60) { 
            result.append(item.getDueDateToString());
            result.append(" | ").append(formattedName).append(" \n");
            //System.out.println(item.getDueDateToString() + " | " + formattedName);
          }
          else {
            //System.out.println(item.getDueDateToString() + " | " +
            //  formattedName.substring(0, 60) + "...");
            result.append(item.getDueDateToString()).append(" | "); 
            result.append(formattedName.substring(0, 60)).append("...\n");
          }
        }
      }
    }
    return result.toString();
  }
  
  /**
   * Prints usage info for DueDates.
   */
  public void printUsageInfo() {
    System.out.println();
    System.out.println("Usage: java -jar duedates.jar -<lender> <field1> <field2>" +
      "[-sort [library | duedate]] [-within <number>]");
    System.out.println();
    System.out.println("Supported lenders are:");
    System.out.println("             UH Manoa Library: -uhm <ID> <LastName>");
    System.out.println("  Hawaii State Public Library: -hsl <ID> <Last4DigitsOfPhoneNumber>");
    System.out.println();
    System.out.println("Example: Query 2 libraries, sort by due date, due within 5 days:");
    System.out.println("  DueDates.jar -uhm 12345678 Test -hsl 12345678 1234 -sort duedate" +
      " -within 5");
  }

  /**
   * The passed email.
   * 
   * @return the emailAddress
   */
  public String getEmailAddress() {
    return emailAddress;
  }

  /**
   * The passed smtp server string.
   * @return the smtpServer
   */
  public String getSmtpServer() {
    return smtpServer;
  }

  /**
   * Returns whether to use console output.
   * 
   * @return Returns boolean.
   */
  public boolean useConsoleOutput() {
    return this.consoleOutput;
  }
  
  /**
   * Returns whether to use email output.
   * 
   * @return Returns boolean.
   */
  public boolean useEmailOutput() {
    return this.emailOutput;
  }
}
